package cn.elegent.lock.consul;
import cn.elegent.lock.ElegentLock;
import cn.elegent.lock.config.LockConfig;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.kv.model.GetValue;
import com.ecwid.consul.v1.kv.model.PutParams;
import com.ecwid.consul.v1.session.model.NewSession;
import com.ecwid.consul.v1.session.model.Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import javax.annotation.PostConstruct;

/**
 * consul 分布式锁处理类
 *      name: true
 *
 */
@ConditionalOnProperty(prefix = "elegent.lock",name = "type",havingValue = "consul")
@Slf4j
public class ConsulLock implements ElegentLock {

    @Autowired
    private LockConfig lockConfig;

    private ConsulClient consulClient;

    @PostConstruct
    public void init(){
        consulClient = new ConsulClient(lockConfig.getHost()[0]);
    }


    @Override
    public boolean isLocked(String lockName) {

        Response<GetValue> kvValue = consulClient.getKVValue(lockName);
        if(kvValue.getValue()==null){
            return false;
        }else{
            return true;
        }
    }

    /**
     * 获取锁
     * @param lockName 锁的名称(key)
     * @param ttlSeconds 锁的超时时间
     * @return
     */
    private boolean tryLock(String lockName,int ttlSeconds){

        try {
            PutParams putParams = new PutParams();
            String sessionId = createSession(lockName, ttlSeconds);
            putParams.setAcquireSession(sessionId);
            //2.将构件好的LockContext存储在consul中
            String key = LockConfig.PREFIX+lockName;
            //consulClient.setKVValue(key, sessionId);
            //加锁成功
            Boolean value = consulClient.setKVValue(key, sessionId, putParams).getValue();
            if(value){
                //开起一个新的线程去执行锁续命--调用看门狗的方式实现
                if(ttlSeconds == -1){  //如果续期
                    new ConsulWatchDog(consulClient).watchSession(sessionId,ttlSeconds);//启动看门狗
                }
                System.out.println("consul加锁成功，锁名称：{}"+lockName);
                return true;
            }else{
                //将sessionId删除
                consulClient.deleteKVValue(key);
                System.out.println("consul加锁失败，锁名称：{}"+lockName);
                return false;
            }
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }


    @Override
    public boolean lock(String lockName, int ttl, boolean isSpin) {

        if(isSpin){  //如果是自旋锁

            while (true){
                if(!isLocked(lockName)){   //如果没有被锁
                    if(tryLock(lockName, ttl)){ //尝试加锁
                        break;//一直到加锁成功才退出
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            return true;

        }else{

            if(isLocked(lockName)){//如果被锁，直接返回false
                System.out.println("consul分布式锁没有被释放,名称:{}"+lockName);
                return false;
            }
            System.out.println( "---------------------" + isLocked(lockName));
            try {
                return tryLock(lockName, ttl);
            }catch (Exception e){
                return false;
            }

        }
    }

    /**
     * 释放锁
     */
    public boolean unLock(String lockName){
        try {
            //先根据锁名称找到对应的sessionId   session 是需要进行存储的  现在我们是通过一个lockContnt 假如说业务上不在一个微服务里释放锁 对应的 其他微服务如何获取到这把锁呢
            String key = LockConfig.PREFIX+lockName;
            Response<GetValue> kvValue = consulClient.getKVValue(key);
            if(kvValue==null) return false;
            GetValue value = kvValue.getValue();
            if(value==null) return false;
            String sessionId = value.getDecodedValue();
            consulClient.sessionDestroy(sessionId, null);// key value也会被删除 是使用的DELETE
            //删除对应的 sessionId存储
            consulClient.deleteKVValue(key);
            System.out.println("consul释放锁成功，锁名称：{}"+lockName);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("consul释放锁出错，锁名称：{}"+lockName);
            return false;
        }
    }

    /**
     * 创建一个sessionId
     * @param lockName
     * @param ttlSeconds
     * @return
     */
    private String createSession(String lockName,int ttlSeconds){
        NewSession session = new NewSession();
        session.setBehavior(Session.Behavior.DELETE);
        session.setName("session " + lockName);
        session.setTtl(ttlSeconds + "s");
        return consulClient.sessionCreate(session, null).getValue();
    }

}
